const util = require('util');
const debug = util.debuglog('oil-game:box:cache');

const {
  countDownNum,   //倒计时
  PRIZE_MAX_NUM,    //宝箱奖品数量
  BOX_OVER_TIME,    //redis前缀，倒计时键
  PRIZE_RESULT,   //redis前缀，奖品
  USER_OVER_TIME_INFO,    //用户当前倒计时信息key键
  boxMaxSize,   //待收取宝箱最大数量
} = require('./consts');
const {
  getOwnBoxInfo,    // 从mongodb中获取已拥有宝箱
  getWaitOpenBoxInfo,   // 从mongodb中获取待领取宝箱
  saveBoxList,   // 同步宝箱数据到数据库
  delBoxByUid,    //清空当前用户的所有宝箱
  initBoxInfo,    //初始化宝箱记录
  syncGiveBoxLog,   //同步宝箱获取记录
  syncOpenBoxLog,   //同步宝箱打开记录
  getTodayBoxOilNumList,    //返回用户当前的石油交易记录，返回[{oil_num}]
  changeOwnBoxToWait,   // mongo中修改宝箱类型为待收取
} = require('./dao');

// 获取已拥有宝箱
exports.getOwnBoxInfo = async function(uid) {
  debug("用户 %s 获取已拥有宝箱", uid);
  //从缓存中获取当前用户
  let user = await this.app.userRemote(uid, 'get', [uid, 'user']);
  if(!user){ 
    debug("用户 %s 获取已拥有宝箱信息，内存中没有此用户，返回null", uid);
    return null;
  }
  //如果内存中有此数据，则直接返回
  if(user.ownOpenBox){ return user.ownOpenBox; }
  //如果内存中不存在该表单数据，则从数据库中查询
  ownBox = await getOwnBoxInfo(uid) || [];
  debug("用户 %s 获取已拥有宝箱，数量  %s  从数据库中获取挂载后返回 %s", uid, ownBox.length, JSON.stringify(ownBox));
  //更新内存中数据
  await this.app.userRemote(uid, "set", [uid, "user.ownOpenBox", ownBox]);
  //返回从数据库查询的数据
  return ownBox;
}

// 获取待领取宝箱
exports.getWaitOpenBoxInfo = async function(uid) {
  debug("用户 %s 获取待领取宝箱", uid);
  //用户离线
  let user = await this.app.userRemote(uid, 'get', [uid, 'user']);
  if(!user){ 
    debug("用户 %s 获取待领取宝箱信息，内存中没有此用户，返回null", uid);
    return null;
  }
  //如果内存中有此数据，则直接返回
  if(user.waitOpenBox){ return user.waitOpenBox; }
  //如果内存中不存在该表单数据，则从数据库中查询
  waitOpenBox = await getWaitOpenBoxInfo(uid) || [];
  debug("用户 %s 获取待领取宝箱，数量  %s  从数据库中获取挂载后返回 %s", uid, waitOpenBox.length, JSON.stringify(waitOpenBox));
  //更新内存中数据
  await this.app.userRemote(uid, "set", [uid, "user.waitOpenBox", waitOpenBox]);
  //返回从数据库查询的数据
  return waitOpenBox;
}

//开始倒计时
exports.startCountDown = function(uid, boxId, sid){
  debug("uid = %s  boxId = %s  sid = %s 开始倒计时，当前时间", uid, boxId, sid, new Date());
  let redis = this.app.get('redis');
  let key = BOX_OVER_TIME+uid+'@'+boxId+'@'+sid;
  let userKey = USER_OVER_TIME_INFO+uid;

  return new Promise((resolve, reject) => {
    redis.set(key, "", function(err, data){   //设置倒计时键
      if(err) return reject(err);
      redis.expire(key, parseInt(countDownNum/1000), function(err, data){    //倒计时键设置超时
        if(err) return reject(err);
        redis.hmset(userKey, "nowCountDownId", boxId, "nowBoxCreated", (new Date()).valueOf(), function(err, data){   //用户倒计时相关信息，宝箱ID和倒计时开始时间
          if(err) return reject(err);
          redis.expire(userKey, parseInt(countDownNum/1000), function(err, data){    //用户倒计时相关信息设置超时
            if(err) return reject(err);
            resolve(data);
          });
        });
      });
    });
  });
}

// 解码宝箱redis超时字段
exports.decodeBoxOverTimeKey = function (key){
  let msgSub = key.slice(BOX_OVER_TIME.length, key.length);
  let msgSubAry = msgSub.split("@");
  let uid = msgSubAry[0];
  let boxId = msgSubAry[1];
  let sid = msgSubAry[2]
  return {uid, boxId, sid}
}

// 获取用户当前正在倒计时的宝箱信息
exports.getCountDownBoxInfo = function(uid) {
  debug("uid = %s  获取用户当前正在倒计时的宝箱信息", uid);
  let redis = this.app.get('redis');
  let userKey = USER_OVER_TIME_INFO + uid;

  return new Promise((resolve, reject) => {
    redis.hgetall(userKey, function (err, data) {
      if (err){ 
        debug("uid = %s  获取用户当前正在倒计时的宝箱信息异常 %s", uid, JSON.stringify(err));
        return reject(err);
      }
      debug("uid = %s  获取用户当前正在倒计时的宝箱信息为 %s", uid, JSON.stringify(data));
      resolve(data);
    });
  });
}

// 停止倒计时
exports.clearCountDown = function(uid, boxId, sid){
  debug("uid = %s  boxId = %s  sid = %s 停止倒计时", uid, boxId, sid);
  let redis = this.app.get('redis');
  let key = BOX_OVER_TIME+uid+'@'+boxId+'@'+sid;    //倒计时的键
  let userKey = USER_OVER_TIME_INFO+uid;

  return new Promise((resolve, reject) => {
    redis.del(key, function(err, data1){
      if(err) return reject(err);
      redis.del(userKey, function(err, data){
        if(err) return reject(err);
        resolve(data)
      });
    });
  });
}

// 将指定的已拥有宝箱放置在待收取宝箱中
exports.ownToWaitOpenBox = async function(uid, boxId){
  debug("uid = %s 将已拥有宝箱 boxId = %s 放置在待收取宝箱中", uid, boxId);
  //将数据初始化到内存中
  let ownBoxInfo = await exports.getOwnBoxInfo.call(this, uid);
  let waitOpenBoxInfo = await exports.getWaitOpenBoxInfo.call(this, uid);
  if(!ownBoxInfo && !waitOpenBoxInfo){    //从数据库中修改已拥有宝箱为待收取
    debug("uid = %s 从数据库中修改宝箱类型", uid);
    await changeOwnBoxToWait.call(this, uid, boxId);
  }else{    
    //从内存中修改已拥有宝箱为待收取
    //2020年7月24日，倒计时结束需要修改的宝箱一次性3个
    ownBoxInfo = ownBoxInfo.filter(item => {
      if(item.id !== boxId) return true;
      item.chest_type = 1;
      item.created_at = (new Date()).valueOf();
      waitOpenBoxInfo.push(item);
      return false;
    });

    //修改二次宝箱，加上上次为三次宝箱
    for(let i = 0; i < 2; i++){
      if(ownBoxInfo.length >= 1 && waitOpenBoxInfo.length + 1 <= boxMaxSize){
        debug("uid = %s 开始修改宝箱", uid);
        let oneOwnBox = ownBoxInfo.pop();
        oneOwnBox.chest_type = 1;
        oneOwnBox.created_at = (new Date()).valueOf();
        waitOpenBoxInfo.push(oneOwnBox);
      } 
    }

    //同步数据
    await this.app.userRemote(uid, "set", [uid, "user.ownOpenBox", ownBoxInfo]);
    await this.app.userRemote(uid, "set", [uid, "user.waitOpenBox", waitOpenBoxInfo]);
  }
}

// 增加一个已拥有宝箱
exports.addOneOwnBox = async function(uid, boxId){
  debug("uid = %s  boxId = %s 增加一个已拥有宝箱", uid, boxId);
  //因首次加载，所以需要初始化已拥有宝箱
  await exports.getOwnBoxInfo.call(this, uid);

  //用户未离线，更改已拥有宝箱为待收取
  await this.app.userRemote(uid, "com", [uid, 
    "user.ownOpenBox.push({ "+
      "id: \"" + boxId + "\","+
      "uid: \"" + uid + "\""+
    "});"
  ]);
}

// 获取待领取宝箱数量
exports.getwaitBoxNum = async function(uid){
  debug("uid = %s 获取待领取宝箱数量", uid);
  //首先从内存中查找待领取宝箱数量
  let waitOpenBox = await exports.getWaitOpenBoxInfo.call(this, uid);
  //其次从数据库查找待领取宝箱数量
  if(!waitOpenBox) waitOpenBox = await getWaitOpenBoxInfo.call(this, uid);
  let waitOpenBoxNum = waitOpenBox ? waitOpenBox.length : 0;
  debug("uid = %s 获取待领取宝箱数量 = %s", uid, waitOpenBoxNum);
  return waitOpenBoxNum;
}

// 删除特定数量待领取宝箱
exports.delManyWaitOpenBox = async function(uid, openBoxLen){
  debug("uid = %s  openBoxLen = %s 删除待领取宝箱", uid, openBoxLen);
  //初始化内存数据
  await exports.getWaitOpenBoxInfo.call(this, uid);
  //删除待领取宝箱
  await this.app.userRemote(uid, "com", [uid, "user.waitOpenBox.splice(0,"+openBoxLen+");" ]);
  //初始化内存数据
  let waitOpenBox = await exports.getWaitOpenBoxInfo.call(this, uid);
}

// 打开宝箱，将奖品暂存
exports.saveTempPrize = async function(uid, prizeAry){
  let redis = this.app.get('redis');
  let key = BOX_OVER_TIME + uid;
  //存储打开宝箱数量
  debug("uid = %s, key = %s, prizeAry = %s,  打开宝箱，将奖品暂存", uid, key, JSON.stringify(prizeAry));

  return new Promise((resolve, reject) => {
    redis.del(key, function(err, data1){   //删除遗留暂存奖品
      if(err){
        debug("uid = %s, 打开宝箱，清除旧的奖品发生异常 %s", uid, JSON.stringify(err));
        return reject(err);
      }
      redis.rpush(key, ...prizeAry.map(item=>item.grade+":"+item.type+":"+item.num), function(err, data){   
        if(err){
          debug("uid = %s, 打开宝箱，将奖品暂存发生异常 %s", uid, JSON.stringify(err));
          return reject(err);
        }
        debug("uid = %s, prizeAry = %s,  打开宝箱，将奖品暂存成功 %s", uid, prizeAry, JSON.stringify(data));
        resolve(data);
      });
    });
  });
}

// 获取用户暂存的奖品
exports.getTempPrize = async function(uid){
  let redis = this.app.get('redis');
  let key = BOX_OVER_TIME + uid;
  //获取打开宝箱数量
  debug("uid = %s, key = %s  获取用户暂存的奖品", uid, key);

  return new Promise((resolve, reject) => {
    redis.lrange(key, 0, -1, function(err, data){   //获取所有的奖品
      if(err){
        debug("uid = %s, 获取用户暂存的奖品发生异常 %s", uid, JSON.stringify(err));
        return reject(err);
      }
      //redis.ltrim(key, data.length+1, -1, function(err, data1){   //删除所有奖品
      redis.del(key, function(err, data1){   //删除所有奖品
        if(err){
          debug("uid = %s, 清理用户暂存的奖品发生异常 %s", uid, JSON.stringify(err));
          return reject(err);
        }
        debug("uid = %s, 已清理用户暂存奖品 %s  \n获取用户暂存的奖品成功 %s", 
          uid, JSON.stringify(data1), JSON.stringify(data));
        resolve(data.map(item=>{
          let itemAry = item.split(":");
          return {
            grade: itemAry[0],
            type: itemAry[1],
            num: itemAry[2],
          };
        }));
      })
    });
  });
}

// 同步宝箱数据到数据库，由PomeloSync调用
exports.syncBoxMemData = async function (uid, ownOpenBox, waitOpenBox){
  debug("用户 %s 同步宝箱数据到数据库, \n已拥有宝箱为 %s，\n待领取宝箱为 %s", uid, JSON.stringify(ownOpenBox), JSON.stringify(waitOpenBox));
  //清空当前用户的所有宝箱
  await delBoxByUid(uid);
  debug("用户 %s 同步宝箱数据到数据库，已清空当前用户的宝箱", uid);
  // 保存宝箱
  let ownIsNotNull = Array.isArray(ownOpenBox) && ownOpenBox.length > 0;
  let waitOpenIsNotNull = Array.isArray(waitOpenBox) && waitOpenBox.length > 0;
  let boxList = [];
  if( ownIsNotNull && waitOpenIsNotNull){
    boxList = [ ...ownOpenBox, ...waitOpenBox ];
  }else if(ownIsNotNull){
    boxList = ownOpenBox;
  }else if(waitOpenIsNotNull){
    boxList = waitOpenBox;
  }
  debug("用户 %s 同步宝箱数据到数据库，需要保存的所有宝箱 %s", uid, JSON.stringify(boxList));
  await saveBoxList(uid, boxList);
}

// 获取当日获取石油数量
exports.getTodayOilNum = async function (uid){
  let allOilOpenLog = await getTodayBoxOilNumList.call(this, uid);
  if(!(Array.isArray(allOilOpenLog) && allOilOpenLog.length > 0)) return 0;
  let allNum = 0;
  for(let i = 0; i < allOilOpenLog.length; i++){
    allNum += parseInt(allOilOpenLog[i].open_log.num);
  }
  debug("uid = %s 获取当日开宝箱获取石油数量 %s", uid, allNum);
  return allNum;
}

// 添加宝箱获取记录
exports.addBoxGiveLog = async function (uid, boxGiveLog){
  //从缓存中获取当前用户
  let user = await this.app.userRemote(uid, 'get', [uid, 'user']);
  if(!user){ 
    debug("用户 %s 添加宝箱获取记录，内存中没有此用户", uid);
    return;
  }
  //初始化内存中该字段
  if(!(Array.isArray(user.give_box_log) && user.give_box_log.length > 0)) 
    await this.app.userRemote(uid, "set", [uid, "user.give_box_log", []]);
  //添加宝箱获取记录
  await this.app.userRemote(uid, "push", [uid, "user.give_box_log", boxGiveLog]);
}

// 添加宝箱打开记录
exports.addBoxOpenLog = async function (uid, boxOpenLog){
  //从缓存中获取当前用户
  let user = await this.app.userRemote(uid, 'get', [uid, 'user']);
  if(!user){ 
    debug("用户 %s 添加宝箱打开记录，内存中没有此用户", uid);
    return;
  }
  //初始化内存中该字段
  if(!(Array.isArray(user.open_box_log) && user.open_box_log.length > 0)) 
    await this.app.userRemote(uid, "set", [uid, "user.open_box_log", []]);
  //添加宝箱打开记录
  if(Array.isArray(boxOpenLog) && boxOpenLog.length > 0)
    await this.app.userRemote(uid, "com", [uid, 
      "user.open_box_log = user.open_box_log.concat(" + JSON.stringify(boxOpenLog) + ");"]);
}

// 同步宝箱到数据库
exports.syncBox = async function (user){
  //当前用户为空
  if(!user) return;
  let { uid, give_box_log, open_box_log } = user;
  debug("uid = %s 开始同步宝箱数据", uid);
  //初始化宝箱数据
  await initBoxInfo(uid);
  //记录宝箱获取记录
  if(Array.isArray(give_box_log) && give_box_log.length > 0){
    debug("uid = %s 开始同步宝箱获取记录 %s ", uid, JSON.stringify(give_box_log));
    syncGiveBoxLog(uid, give_box_log);
  }
  //记录宝箱打开记录
  if(Array.isArray(open_box_log) && open_box_log.length > 0){
    debug("uid = %s 开始同步宝箱打开记录 %s ", uid, JSON.stringify(open_box_log));
    syncOpenBoxLog(uid, open_box_log);
  }
}
